#include "sht30_i2c.h"

#include <string.h>

#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"

static const char *TAG = "SHT30_I2C";

// SHT30命令定义
#define SHT30_CMD_MEASURE_HIGH_REPEAT_STRETCH 0x2C06  // 高精度测量，时钟拉伸
#define SHT30_CMD_MEASURE_HIGH_REPEAT_NO_STRETCH \
  0x2400                              // 高精度测量，无时钟拉伸
#define SHT30_CMD_SOFT_RESET 0x30A2   // 软复位
#define SHT30_CMD_READ_STATUS 0xF32D  // 读取状态寄存器

// 全局变量保存配置
static i2c_port_t g_i2c_port;
static uint8_t g_dev_addr;
static bool g_is_initialized = false;

// CRC校验表（用于CRC-8计算）
static const uint8_t CRC_POLYNOMIAL = 0x31;

/**
 * @brief 计算CRC-8校验值
 *
 * @param data 数据
 * @param len 数据长度
 * @return uint8_t CRC值
 */
static uint8_t sht30_calculate_crc(const uint8_t *data, uint8_t len) {
  uint8_t crc = 0xFF;

  for (uint8_t i = 0; i < len; i++) {
    crc ^= data[i];
    for (uint8_t bit = 8; bit > 0; --bit) {
      if (crc & 0x80) {
        crc = (crc << 1) ^ CRC_POLYNOMIAL;
      } else {
        crc = (crc << 1);
      }
    }
  }

  return crc;
}

/**
 * @brief 发送命令到SHT30
 *
 * @param cmd 命令
 * @return esp_err_t
 */
static esp_err_t sht30_send_command(uint16_t cmd) {
  uint8_t cmd_buffer[2];
  cmd_buffer[0] = cmd >> 8;    // 高字节
  cmd_buffer[1] = cmd & 0xFF;  // 低字节

  i2c_cmd_handle_t cmd_handle = i2c_cmd_link_create();
  i2c_master_start(cmd_handle);
  i2c_master_write_byte(cmd_handle, (g_dev_addr << 1) | I2C_MASTER_WRITE, true);
  i2c_master_write(cmd_handle, cmd_buffer, 2, true);
  i2c_master_stop(cmd_handle);

  esp_err_t ret =
      i2c_master_cmd_begin(g_i2c_port, cmd_handle, 1000 / portTICK_PERIOD_MS);
  i2c_cmd_link_delete(cmd_handle);

  return ret;
}

/**
 * @brief 检查SHT30是否存在
 *
 * @return true 存在
 * @return false 不存在
 */
static bool sht30_is_present(void) {
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  i2c_master_start(cmd);
  i2c_master_write_byte(cmd, (g_dev_addr << 1) | I2C_MASTER_WRITE, true);
  i2c_master_stop(cmd);

  esp_err_t ret =
      i2c_master_cmd_begin(g_i2c_port, cmd, 100 / portTICK_PERIOD_MS);
  i2c_cmd_link_delete(cmd);

  return (ret == ESP_OK);
}

sht30_err_t sht30_init(const sht30_config_t *config) {
  if (config == NULL) {
    ESP_LOGE(TAG, "Configuration is NULL");
    return SHT30_ERR_INVALID_DATA;
  }

  // 保存配置
  g_i2c_port = config->i2c_port;
  g_dev_addr = config->dev_addr;

  // 配置I2C
  i2c_config_t i2c_conf = {.mode = I2C_MODE_MASTER,
                           .sda_io_num = config->sda_gpio,
                           .scl_io_num = config->scl_gpio,
                           .sda_pullup_en = GPIO_PULLUP_ENABLE,
                           .scl_pullup_en = GPIO_PULLUP_ENABLE,
                           .master.clk_speed = config->freq_hz};

  esp_err_t ret = i2c_param_config(g_i2c_port, &i2c_conf);
  if (ret != ESP_OK) {
    ESP_LOGE(TAG, "I2C parameter configuration failed");
    return SHT30_ERR_I2C_COMM_FAILED;
  }

  ret = i2c_driver_install(g_i2c_port, I2C_MODE_MASTER, 0, 0, 0);
  if (ret != ESP_OK) {
    ESP_LOGE(TAG, "I2C driver installation failed");
    return SHT30_ERR_I2C_COMM_FAILED;
  }

  // 检查设备是否存在
  if (!sht30_is_present()) {
    ESP_LOGE(TAG, "SHT30 not found at address 0x%02X", g_dev_addr);
    i2c_driver_delete(g_i2c_port);
    return SHT30_ERR_NOT_FOUND;
  }

  // 软复位SHT30
  ret = sht30_send_command(SHT30_CMD_SOFT_RESET);
  if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to reset SHT30");
    i2c_driver_delete(g_i2c_port);
    return SHT30_ERR_I2C_COMM_FAILED;
  }

  // 等待复位完成
  vTaskDelay(10 / portTICK_PERIOD_MS);

  g_is_initialized = true;
  ESP_LOGI(TAG, "SHT30 initialized successfully");

  return SHT30_OK;
}

sht30_err_t sht30_read_data(float *temperature, float *humidity) {
  if (!g_is_initialized) {
    ESP_LOGE(TAG, "SHT30 not initialized");
    return SHT30_ERR_I2C_COMM_FAILED;
  }

  if (temperature == NULL || humidity == NULL) {
    ESP_LOGE(TAG, "Invalid parameters");
    return SHT30_ERR_INVALID_DATA;
  }

  // 发送测量命令（高精度，无时钟拉伸）
  esp_err_t ret = sht30_send_command(SHT30_CMD_MEASURE_HIGH_REPEAT_NO_STRETCH);
  if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to send measurement command");
    return SHT30_ERR_I2C_COMM_FAILED;
  }

  // 等待测量完成（根据数据手册，高精度测量需要约15ms）
  vTaskDelay(20 / portTICK_PERIOD_MS);

  // 读取测量结果（6字节：温度高字节、温度低字节、温度CRC、湿度高字节、湿度低字节、湿度CRC）
  uint8_t data[6];
  i2c_cmd_handle_t cmd = i2c_cmd_link_create();
  i2c_master_start(cmd);
  i2c_master_write_byte(cmd, (g_dev_addr << 1) | I2C_MASTER_READ, true);
  i2c_master_read(cmd, data, 6, I2C_MASTER_LAST_NACK);
  i2c_master_stop(cmd);

  ret = i2c_master_cmd_begin(g_i2c_port, cmd, 1000 / portTICK_PERIOD_MS);
  i2c_cmd_link_delete(cmd);

  if (ret != ESP_OK) {
    ESP_LOGE(TAG, "Failed to read measurement data");
    return SHT30_ERR_I2C_COMM_FAILED;
  }

  // 验证温度数据的CRC
  uint8_t temp_crc = sht30_calculate_crc(data, 2);
  if (temp_crc != data[2]) {
    ESP_LOGE(TAG,
             "Temperature CRC check failed: calculated 0x%02X, received 0x%02X",
             temp_crc, data[2]);
    return SHT30_ERR_CRC_FAILED;
  }

  // 验证湿度数据的CRC
  uint8_t hum_crc = sht30_calculate_crc(data + 3, 2);
  if (hum_crc != data[5]) {
    ESP_LOGE(TAG,
             "Humidity CRC check failed: calculated 0x%02X, received 0x%02X",
             hum_crc, data[5]);
    return SHT30_ERR_CRC_FAILED;
  }

  // 计算温度值（公式来自SHT30数据手册）
  uint16_t temp_raw = ((uint16_t)data[0] << 8) | data[1];
  *temperature = -45.0f + 175.0f * temp_raw / 65535.0f;

  // 计算湿度值（公式来自SHT30数据手册）
  uint16_t hum_raw = ((uint16_t)data[3] << 8) | data[4];
  *humidity = 100.0f * hum_raw / 65535.0f;

  // ESP_LOGI(TAG, "Temperature: %.2f°C, Humidity: %.2f%%", *temperature,
  // *humidity);

  return SHT30_OK;
}